<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Camera Interaction App</title>
    <style>
        body {
            font-family: sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 20px;
            padding: 20px;
            background-color: #f0f0f0;
        }
        .controls, .io-areas {
            display: flex;
            gap: 10px;
            align-items: center;
            background-color: #fff;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        .io-areas {
            flex-direction: column;
            align-items: stretch;
        }
        textarea {
            width: 300px;
            height: 80px;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 14px;
        }
        #videoFeed {
            width: 480px;
            height: 360px;
            border: 2px solid #333;
            background-color: #000;
            border-radius: 8px;
        }
        #startButton {
            padding: 10px 20px;
            font-size: 16px;
            cursor: pointer;
            border: none;
            border-radius: 4px;
            color: white;
        }
        #startButton.start {
            background-color: #28a745; /* Green */
        }
        #startButton.stop {
            background-color: #dc3545; /* Red */
        }
        label {
            font-weight: bold;
        }
        select {
            padding: 8px;
            border-radius: 4px;
            border: 1px solid #ccc;
        }
        .hidden {
            display: none;
        }
    </style>
</head>
<body>

    <h1>Camera Interaction App</h1>

    <video id="videoFeed" autoplay playsinline></video>
    <canvas id="canvas" class="hidden"></canvas> <!-- For capturing frames -->

    <div class="io-areas">
        <div>
            <label for="baseURL">Base API:</label><br>
            <input id="baseURL" name="Instruction" value="http://localhost:8080"></textarea>
        </div>
        <div>
            <label for="instructionText">Instruction:</label><br>
            <textarea id="instructionText" style="height: 2em; width: 40em" name="Instruction"></textarea>
        </div>
        <div>
            <label for="responseText">Response:</label><br>
            <textarea id="responseText" style="height: 2em; width: 40em" name="Response" readonly placeholder="Server response will appear here..."></textarea>
        </div>
    </div>

    <div class="controls">
        <label for="intervalSelect">Interval between 2 requests:</label>
        <select id="intervalSelect" name="Interval between 2 requests">
            <option value="100">100ms</option>
            <option value="250">250ms</option>
            <option value="500" selected>500ms</option>
            <option value="1000">1s</option>
            <option value="2000">2s</option>
        </select>
        <button id="startButton" class="start">Start</button>
    </div>

    <script>
        const video = document.getElementById('videoFeed');
        const canvas = document.getElementById('canvas');
        const baseURL = document.getElementById('baseURL');
        const instructionText = document.getElementById('instructionText');
        const responseText = document.getElementById('responseText');
        const intervalSelect = document.getElementById('intervalSelect');
        const startButton = document.getElementById('startButton');

        instructionText.value = "What do you see?"; // default instruction

        let stream;
        let intervalId;
        let isProcessing = false;

        // Returns response text (string)
        async function sendChatCompletionRequest(instruction, imageBase64URL) {
            const response = await fetch(`${baseURL.value}/v1/chat/completions`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    max_tokens: 100,
                    messages: [
                        { role: 'user', content: [
                            { type: 'text', text: instruction },
                            { type: 'image_url', image_url: {
                                url: imageBase64URL,
                            } }
                        ] },
                    ]
                })
            });
            if (!response.ok) {
                const errorData = await response.text();
                return `Server error: ${response.status} - ${errorData}`;
            }
            const data = await response.json();
            return data.choices[0].message.content;
        }

        // 1. Ask for camera permission on load
        async function initCamera() {
            try {
                stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
                video.srcObject = stream;
                responseText.value = "Camera access granted. Ready to start.";
            } catch (err) {
                console.error("Error accessing camera:", err);
                responseText.value = `Error accessing camera: ${err.name} - ${err.message}. Please ensure permissions are granted and you are on HTTPS or localhost.`;
                alert(`Error accessing camera: ${err.name}. Make sure you've granted permission and are on HTTPS or localhost.`);
            }
        }

        function captureImage() {
            if (!stream || !video.videoWidth) {
                console.warn("Video stream not ready for capture.");
                return null;
            }
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
            const context = canvas.getContext('2d');
            context.drawImage(video, 0, 0, canvas.width, canvas.height);
            return canvas.toDataURL('image/jpeg', 0.8); // Use JPEG for smaller size, 0.8 quality
        }

        async function sendData() {
            if (!isProcessing) return; // Ensure we don't have overlapping requests if processing takes longer than interval

            const instruction = instructionText.value;
            const imageBase64URL = captureImage();

            if (!imageBase64URL) {
                responseText.value = "Failed to capture image. Stream might not be active.";
                // Optionally stop processing if image capture fails consistently
                // handleStop();
                return;
            }

            const payload = {
                instruction: instruction,
                imageBase64URL: imageBase64URL
            };

            try {
                const response = await sendChatCompletionRequest(payload.instruction, payload.imageBase64URL);
                responseText.value = response;
            } catch (error) {
                console.error('Error sending data:', error);
                responseText.value = `Error: ${error.message}`;
            }
        }

        function handleStart() {
            if (!stream) {
                responseText.value = "Camera not available. Cannot start.";
                alert("Camera not available. Please grant permission first.");
                return;
            }
            isProcessing = true;
            startButton.textContent = "Stop";
            startButton.classList.remove('start');
            startButton.classList.add('stop');

            instructionText.disabled = true;
            intervalSelect.disabled = true;

            responseText.value = "Processing started...";

            const intervalMs = parseInt(intervalSelect.value, 10);
            
            // Initial immediate call
            sendData(); 
            
            // Then set interval
            intervalId = setInterval(sendData, intervalMs);
        }

        function handleStop() {
            isProcessing = false;
            if (intervalId) {
                clearInterval(intervalId);
                intervalId = null;
            }
            startButton.textContent = "Start";
            startButton.classList.remove('stop');
            startButton.classList.add('start');

            instructionText.disabled = false;
            intervalSelect.disabled = false;
            if (responseText.value.startsWith("Processing started...")) {
                responseText.value = "Processing stopped.";
            }
        }

        startButton.addEventListener('click', () => {
            if (isProcessing) {
                handleStop();
            } else {
                handleStart();
            }
        });

        // Initialize camera when the page loads
        window.addEventListener('DOMContentLoaded', initCamera);

        // Optional: Stop stream when page is closed/navigated away to release camera
        window.addEventListener('beforeunload', () => {
            if (stream) {
                stream.getTracks().forEach(track => track.stop());
            }
            if (intervalId) {
                clearInterval(intervalId);
            }
        });

    </script>
</body>
</html>